iT邦幫忙

2024 iThome 鐵人賽

DAY 17
3
DevOps

後 Grafana 時代的自我修養系列 第 17

後 Grafana 時代的第十七天 - Gafana IaC 實戰 - DataSource

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20241001/20149562mdrNKSAoa4.png

引言

在現代監控和可觀測性領域,Grafana 已成為不可或缺的視覺化平台。隨著組織規模的擴大和監控需求的增加,有效管理 Grafana 的資料來源變得越來越具有挑戰性。本文將開始深入探討如何利用 Terraform 實現基礎設施即程式碼(IaC)的方法,設計一個強大的頂層結構來管理 Grafana 資料來源。

為什麼選擇 Terraform 進行 IaC?

在深入技術細節之前,讓我們再次強調為什麼選擇 Terraform 進行 Grafana 資料來源的 IaC 管理:

  1. 一致性:確保所有環境中的資料來源設定保持一致。
  2. 版本控制:輕鬆追蹤設定更改,並在需要時回滾。
  3. 自動化:簡化資料來源的新增、修改和刪除過程。
  4. 可重複性:輕鬆在不同環境中複製設定。
  5. 協作:促進團隊成員之間的協作和知識共享。

Datasource 頂層結構設計

我們的頂層結構設計基於兩個核心概念:設定模板(Configs)和資料源(Datasources)。

https://ithelp.ithome.com.tw/upload/images/20241001/20149562GGQVYTuVYd.png

1. 設定模板(Configs)

設定模板定義了可重複使用的資料來源設定。這種方法允許我們創建高度標準化的設定,可以跨多個資料來源實例共享。

設定範本(Configs)
configs 部分定義了可重複使用的資料來源設定範本:

configs = {
  prometheus_default = { ... }
  prometheus_custom = { ... }
  loki_default = { ... }
  tempo_default = { ... }
}

每個設定範本包含 json_data 和 secure_json_data,允許我們定義資料來源的具體設定和敏感資訊。

2. 資料來源實例(Datasources)

datasources 部分定義了實際的資料來源實例:

  datasources = {
    // Prometheus Datasource
    prometheus = {
      Prometheus-1 = {
        uid        = "prometheus"
        url        = "http://prometheus:9090"
        is_default = true
        config     = local.configs.prometheus_default
      }
      Prometheus-2 = {
        uid    = "prometheus-2"
        url    = "http://prometheus-2:9090"
        config = local.configs.prometheus_default
      }
    }
    // Loki Datasource
    loki = {
      Loki-default = {
        uid    = "loki"
        url    = "http://loki:3100"
        config = local.configs.loki_default
      }
    }
    // Tempo Datasource
    tempo = {
      Tempo-default = {
        uid    = "tempo"
        url    = "http://tempo:3200"
        config = local.configs.tempo_default
      }
    }
  }

這個結構的設計採用了多層嵌套的 map 結構,具有以下特點和優勢:

  1. 資料源分類:
    最外層的 map 使用資料源類型(如 prometheus、loki、tempo)作為 key,這樣可以清晰地組織不同類型的資料源。
  2. 資源命名:
    每種資料源類型下,可以定義多個具名實例(如 Prometheus-1、Prometheus-2)。這允許在同一類型中管理多個資料源,並給予它們有意義的名稱。
  3. 設定復用:
    通過 config = local.configs.prometheus_default 這樣的引用,可以輕鬆地在多個資料源之間共享設定。這減少了重複代碼,提高了維護性。
  4. 可擴展性:
    這種結構使得添加新的資料源類型或實例變得簡單,只需在適當的層級添加新的 map 項目即可。

這種結構允許我們為每種類型的資料源定義多個設定,每個資源都可以引用同一個設定範本。

實戰演練

現在,我們已經瞭解了 Grafana 中不同類型的資料源及其設定,接下來我們將結合先前所提到的全域動態生成資源的概念,實作我們的 Grafana 資料源 IaC 解決方案。這種方法將允許我們靈活地管理多種類型的資料源,並為每個資料源提供自定義的設定選項。

接下來我們實際執行指令建立 grafana_data_source 資源:

locals {
  datasources = {
    // Prometheus Datasource
    prometheus = {
      Prometheus-1 = {
        uid        = "prometheus"
        url        = "http://prometheus:9090"
        http_headers = {
          "X-Scope-OrgID" = "1"
        }
        is_default = true
        config     = local.configs.prometheus_default
      }
      Prometheus-2 = {
        uid    = "prometheus-2"
        url    = "http://prometheus-2:9090"
        http_headers = {
          "Authorization" = "Bearer your.prometheus_token"
        }
        config = local.configs.prometheus_default
      }
    }
    // Loki Datasource
    loki = {
      Loki-default = {
        uid    = "loki"
        url    = "http://loki:3100"
        http_headers = {
          "X-Custom-Header" = "LokiValue"
        }
        config = local.configs.loki_default
      }
    }
    // Tempo Datasource
    tempo = {
      Tempo-default = {
        uid    = "tempo"
        url    = "<http://tempo:3200>"
        config = local.configs.tempo_default
      }
    }
  }

  configs = {
    prometheus_default = {
      json_data = {
        http_method     = "POST"
        sigv4_auth      = false
        sigv4_auth_type = ""
        sigv4_region    = ""
      }
      secure_json_data = {}
    }
    prometheus_custom = {
      json_data = {
        http_method     = "GET"
        sigv4_auth      = true
        sigv4_auth_type = "default"
        sigv4_region    = "us-west-2"
      }
      secure_json_data = {
        access_key = "your-access-key"
        secret_key = "your-secret-key"
      }
    }
    loki_default = {
      json_data = {
        max_lines = 1000
        derived_fields = [
          {
            datasourceUid = "tempo"
            matcherRegex  = "[tT]race_?[iI][dD]\\"?[:=]\\"?(\\\\w+)"
            name          = "TraceID"
            url           = "$${__value.raw}"
          }
        ]
      }
      secure_json_data = {}
    }
    tempo_default = {
      json_data = {
        tracesToLogsV2 = {
          customQuery     = true
          datasourceUid   = "loki"
          filterBySpanID  = false
          filterByTraceID = false
          query           = "|=\\"$${__trace.traceId}\\" | json"
        }
      }
      secure_json_data = {}
    }
  }
}

resource "grafana_data_source" "datasources" {
  for_each = merge([
    for type, sources in local.datasources : {
      for name, source in sources : "${type}/${name}" => merge(source, { name = name, type = type })
    }
  ]...)

  type         = each.value.type
  name         = each.value.name
  uid          = each.value.uid
  url          = each.value.url
  is_default   = lookup(each.value, "is_default", false)
  http_headers = lookup(each.value, "http_headers", null)

  json_data_encoded = jsonencode(each.value.config.json_data)
  secure_json_data_encoded = jsonencode(each.value.config.secure_json_data)
}

這個 Terraform 資源展示了幾個進階技巧。它利用 for_each 和 merge 函數動態生成多個資料源,採用嵌套 map 結構組織配置,提高了代碼的可讀性和可維護性。通過 lookup 函數處理可選屬性,以及 jsonencode 函數處理複雜的 JSON 配置,使得資源定義更加靈活。這種方法大大簡化了 Grafana 資料源的管理,使得大規模部署和維護變得更加高效。

接著就讓我們實際執行指令建立資源:

terraform init
terraform apply
---
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # grafana_data_source.datasources["loki/Loki-default"] will be created
  + resource "grafana_data_source" "datasources" {
      + access_mode              = "proxy"
      + basic_auth_enabled       = false
      + http_headers             = (sensitive value)
      + id                       = (known after apply)
      + is_default               = false
      + json_data_encoded        = jsonencode(
        // ...
        )
      + name                     = "Loki-default"
      + secure_json_data_encoded = (sensitive value)
      + type                     = "loki"
      + uid                      = "loki"
      + url                      = "<http://loki:3100>"
    }

  # grafana_data_source.datasources["prometheus/Prometheus-1"] will be created
  + resource "grafana_data_source" "datasources" {
      + access_mode              = "proxy"
      + basic_auth_enabled       = false
      + http_headers             = (sensitive value)
      + id                       = (known after apply)
      + is_default               = true
      + json_data_encoded        = jsonencode(
        // ...
        )
      + name                     = "Prometheus-1"
      + secure_json_data_encoded = (sensitive value)
      + type                     = "prometheus"
      + uid                      = "prometheus"
      + url                      = "<http://prometheus:9090>"
    }

  # grafana_data_source.datasources["prometheus/Prometheus-2"] will be created
  + resource "grafana_data_source" "datasources" {
      + access_mode              = "proxy"
      + basic_auth_enabled       = false
      + http_headers             = (sensitive value)
      + id                       = (known after apply)
      + is_default               = false
      + json_data_encoded        = jsonencode(
        // ...
        )
      + name                     = "Prometheus-2"
      + secure_json_data_encoded = (sensitive value)
      + type                     = "prometheus"
      + uid                      = "prometheus-2"
      + url                      = "<http://prometheus-2:9090>"
    }

  # grafana_data_source.datasources["tempo/Tempo-default"] will be created
  + resource "grafana_data_source" "datasources" {
      + access_mode              = "proxy"
      + basic_auth_enabled       = false
      + id                       = (known after apply)
      + is_default               = false
      + json_data_encoded        = jsonencode(
        // ...
        )
      + name                     = "Tempo-default"
      + secure_json_data_encoded = (sensitive value)
      + type                     = "tempo"
      + uid                      = "tempo"
      + url                      = "<http://tempo:3200>"
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

grafana_data_source.datasources["tempo/Tempo-default"]: Creating...
grafana_data_source.datasources["loki/Loki-default"]: Creating...
grafana_data_source.datasources["prometheus/Prometheus-1"]: Creating...
grafana_data_source.datasources["prometheus/Prometheus-2"]: Creating...
grafana_data_source.datasources["tempo/Tempo-default"]: Creation complete after 0s [id=1:tempo]
grafana_data_source.datasources["loki/Loki-default"]: Creation complete after 0s [id=1:loki]
grafana_data_source.datasources["prometheus/Prometheus-1"]: Creation complete after 0s [id=1:prometheus]
grafana_data_source.datasources["prometheus/Prometheus-2"]: Creation complete after 0s [id=1:prometheus-2]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

接著我們到 Grafana 中確認是否成功建立:

  • 在 Data sources 頁面中,能看到剛剛建立的資料源:

https://ithelp.ithome.com.tw/upload/images/20241001/20149562z4Fn72P4Il.png

  • 點選其中一個 Prometheus-1 進入資料源頁面,就能看到我們指定的內容被正確設定:

https://ithelp.ithome.com.tw/upload/images/20241001/20149562up0t3V8mxL.png

結論

在這個快速變化的技術世界中,我們的 Grafana 資料源管理方法不僅僅是一種技術實現,更是一種思維模式的轉變。通過將基礎設施即代碼(IaC)的理念與 Grafana 的強大功能相結合,我們開闢了一條通向更彈性的監控系統管理之路,為CI/CD 流程注入了更多可能性。

更重要的是,隨著資料量的爆炸性增長和監控需求的日益複雜化,這種方法提供了一個可擴展的框架,能夠輕鬆適應新的資料源類型和不斷演變的監控策略。它不僅提高了我們對系統的掌控能力,還為數據驅動決策提供了堅實的基礎,使得從海量數據中提取有價值的洞察變得更加容易。

Reference


上一篇
後 Grafana 時代的第十六天 - Gafana IaC 實戰 - Organization、Team、User
下一篇
後 Grafana 時代的第十八天 - Gafana IaC 實戰 - Dashboard、Folder
系列文
後 Grafana 時代的自我修養31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言